using Aurora.TaskScheduler.Abstractions.BaseModel;
using Aurora.TaskScheduler.Abstractions.Message;
using Aurora.TaskScheduler.Abstractions.Storage;
using Aurora.TaskScheduler.Extension.Extensions;
using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Spi;

namespace Aurora.TaskScheduler.Extension.SchedulerImpl;

/// <summary>
/// quartz task 处理器
/// </summary>
public class QuartzTaskHandler : ITaskHandler
{
    private readonly ISchedulerFactory _schedulerFactory;
    private readonly IDataStorage _storage;
    private readonly IJobFactory _jobFactory;

    public QuartzTaskHandler(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IDataStorage storage)
    {
        _schedulerFactory = schedulerFactory;
        _jobFactory = jobFactory;
        _storage = storage;
    }

    /// <summary>
    /// 开启任务
    /// </summary>
    /// <param name="task"></param>
    /// <returns></returns>
    public async Task<bool> Start(AppTask task)
    {
        var isJob = await IsQuartzJob(task.TaskName, task.GroupName);
        var taskModel = await _storage.GetTaskByName(task.TaskName, task.GroupName);
        if (isJob.success)
        {
            //开始移除触发器
            IScheduler scheduler = await _schedulerFactory.GetScheduler();

            ITrigger trigger = await JobExtension.FindTrigger(scheduler, task.GroupName, task.TaskName);

            await scheduler.ResumeTrigger(trigger.Key);
        }
        else
        {
            IScheduler scheduler = await _schedulerFactory.GetScheduler();
            IJobDetail job = JobExtension.CreateJobDetail(task);
            ITrigger trigger = JobExtension.CreateTrigger(task);

            if (_jobFactory != null)
            {
                scheduler.JobFactory = _jobFactory;
            }

            await scheduler.ScheduleJob(job, trigger);
            await scheduler.Start();
        }

        if (taskModel != null)
        {
            task.Status = (int)JobState.开启;
            await _storage.Update(task);
        }

        return true;
    }

    public async Task<bool> Run(AppTask task)
    {
        var isJob = await IsQuartzJob(task.TaskName, task.GroupName);
        if (isJob.success)
        {
            IScheduler scheduler = await _schedulerFactory.GetScheduler();

            JobKey jobKey = await JobExtension.FindJobKey(scheduler: scheduler, task.GroupName, task.TaskName);

            await scheduler.TriggerJob(jobKey);
            return true;
        }

        return false;
    }

    /// <summary>
    /// 添加任务-不自动开始
    /// </summary>
    /// <param name="task"></param>
    /// <returns></returns>
    public async Task<bool> AddJobNoStart(AppTask task)
    {
        var model = await _storage.GetTaskByName(task.TaskName, task.GroupName);
        if (model != null)
        {
            // 做更新
            task.Id = model.Id;
            task.CreatedTime = model.CreatedTime;
            await _storage.Update(task);
            return true;
        }

        var taskId = await _storage.Add(task);
        if (string.IsNullOrEmpty(taskId))
            return false;
        return true;
    }

    public async Task<bool> AddJobNoStart(List<AppTask> tasks)
    {
        var listByTaskType = await _storage.GetListByTaskType(1);
        List<AppTask> updateList = new List<AppTask>();
        List<AppTask> addList = new List<AppTask>();
        foreach (var item in tasks)
        {
            var task = listByTaskType.FirstOrDefault(x =>
                x.GroupName.Equals(item.GroupName) && x.DllClassName.Equals(item.DllClassName));
            if (task != null)
            {
                // 做更新
                item.Id = task.Id;
                item.CreatedTime = task.CreatedTime;
                updateList.Add(item);
            }
            else
            {
                item.Id = Guid.NewGuid().ToString();
                item.CreatedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                addList.Add(item);
            }
        }

        if (addList.Count() > 0)
        {
            await _storage.Add(addList);
        }

        if (updateList.Count() > 0)
        {
            await _storage.Update(updateList);
        }

        return true;
    }

    /// <summary>
    /// 运行数据库中的任务
    /// </summary>
    public async Task InitJobs()
    {
        var jobs = await GetJobs();
        IScheduler scheduler = await _schedulerFactory.GetScheduler();
        foreach (var item in jobs)
        {
            try
            {
                IJobDetail job = JobExtension.CreateJobDetail(item);
                ITrigger trigger = JobExtension.CreateTrigger(item);
                if (_jobFactory != null)
                {
                    scheduler.JobFactory = _jobFactory;
                }

                if (item.Status == (int)JobState.开启)
                {
                    await scheduler.ScheduleJob(job, trigger);
                    await AddLog(new AppTaskLog()
                    {
                        TaskName = item.TaskName,
                        GroupName = item.GroupName,
                        BeginDate = DateTime.Now,
                        TaskId = item.Id,
                        Success = true,
                        CreateTime = DateTime.Now,
                        Msg = $"任务初始化启动成功:{item.Status}"
                    });
                }
                else
                {
                    await scheduler.ScheduleJob(job, trigger);
                    await Pause(item);
                }
            }
            catch (Exception ex)
            {
                await AddLog(new AppTaskLog()
                {
                    TaskName = item.TaskName, GroupName = item.GroupName,
                    TaskId = item.Id,
                    Success = false,
                    CreateTime = DateTime.Now,
                    Msg = $"任务初始化未启动,出现异常,异常信息{ex.Message}"
                });
                continue;
            }

            await scheduler.Start();
        }
    }

    public async Task<List<AppTask>> GetJobs(bool containerOne = false)
    {
        var list = await _storage.GetAllJob(containerOne);
        var schedule = await _schedulerFactory.GetScheduler();
        var groups = await schedule.GetJobGroupNames();
        foreach (var group in groups)
        {
            var jobKeys = await schedule.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(group));
            foreach (JobKey jobKey in jobKeys)
            {
                var taskOptions = list.FirstOrDefault(x => x.GroupName.Equals(jobKey.Group)
                                                           && x.TaskName.Equals(jobKey.Name));
                if (taskOptions == null)
                    continue;
                var triggers = await schedule.GetTriggersOfJob(jobKey);
                foreach (var trigger in triggers)
                {
                    DateTimeOffset? dateTimeOffset = trigger.GetPreviousFireTimeUtc();
                    DateTimeOffset? nextDateTimeOffset = trigger.GetNextFireTimeUtc();
                    if (dateTimeOffset != null)
                    {
                        var tmp = dateTimeOffset.Value.LocalDateTime;
                        taskOptions.LastRunTime = tmp;
                    }
                    else
                    {
                        var runLog = await _storage.GetLastLog(taskOptions.TaskName, taskOptions.GroupName);
                        if (runLog != null)
                        {
                            taskOptions.LastRunTime = runLog.BeginDate;
                        }
                    }

                    if (nextDateTimeOffset != null)
                    {
                        var tmp = nextDateTimeOffset.Value.LocalDateTime;
                        taskOptions.NextRunTime = tmp;
                    }
                }
            }
        }

        return list;
    }


    /// <summary>
    /// 添加任务
    /// </summary>
    /// <param name="task"></param>
    /// <returns></returns>
    public async Task<string> Add(AppTask task)
    {
        if (!task.TaskExecuteType.Equals("ONE"))
            IsValidExpression(task.Interval);
        string id = "";
        var model = await _storage.GetTaskByName(task.TaskName, task.GroupName);
        if (model != null)
        {
            if ("ONE".Equals(model.TaskExecuteType))
            {
                await _storage.Remove(model);
            }
            else
            {
                // 做更新
                task.Id = model.Id;
                task.CreatedTime = model.CreatedTime;
                await _storage.Update(task);
                id = task.Id;
                return "-1";
            }
        }

        if (string.IsNullOrWhiteSpace(id))
            id = await _storage.Add(task);


        if (string.IsNullOrEmpty(id))
            return string.Empty;
        //开始插入到 定时任务里
        IJobDetail job = JobExtension.CreateJobDetail(task);
        ITrigger trigger = JobExtension.CreateTrigger(task);

        var scheduler = await _schedulerFactory.GetScheduler();
        if (_jobFactory != null)
            scheduler.JobFactory = _jobFactory;
        //开启才加入Schedule中,如果加入在暂停而定时任务执行过快,会导致卡死
        if (task.Status == (int)JobState.开启)
        {
            await scheduler.ScheduleJob(job, trigger);
            if (!scheduler.IsStarted)
            {
                await scheduler.Start();
            }
        }
        else
        {
            await Pause(task);
            await AddLog(new AppTaskLog()
            {
                TaskName = task.TaskName, GroupName = task.GroupName,
                Msg = $"任务新建,未启动,状态为:{task.Status}"
            });
        }

        return task.Id;
    }

    /// <summary>
    /// 添加log
    /// </summary>
    /// <param name="log"></param>
    private async Task AddLog(AppTaskLog log)
    {
        await _storage.AddLog(log);
    }

    /// <summary>
    /// 更新任务
    /// </summary>
    /// <param name="task"></param>
    /// <returns></returns>
    public async Task<bool> Update(AppTask task)
    {
        var isJob = await IsQuartzJob(task.TaskName, task.GroupName);
        var taskModel = await _storage.GetDetail(task.Id);
        if (isJob.success)
        {
            //开始移除触发器
            IScheduler scheduler = await _schedulerFactory.GetScheduler();

            ITrigger triggerOld = await JobExtension.FindTrigger(scheduler, task.GroupName, task.TaskName);

            await scheduler.PauseTrigger(triggerOld.Key);
            await scheduler.UnscheduleJob(triggerOld.Key); // 移除触发器
            await scheduler.DeleteJob(triggerOld.JobKey);
            IJobDetail job = JobExtension.CreateJobDetail(task);
            ITrigger triggernew = JobExtension.CreateTrigger(task, true);


            if (_jobFactory != null)
            {
                scheduler.JobFactory = _jobFactory;
            }

            await scheduler.ScheduleJob(job, triggernew);
            if (task.Status == (int)JobState.开启)
            {
                await scheduler.Start();
            }
            else
            {
                await scheduler.PauseTrigger(triggernew.Key);
                await AddLog(new AppTaskLog()
                {
                    TaskName = task.TaskName,
                    GroupName = task.GroupName,
                    Msg = $"任务新建,未启动,状态为:{task.Status}"
                });
            }
        }

        if (taskModel != null)
        {
            await _storage.Update(task);
        }

        return true;
    }


    /// <summary>
    /// 移除任务
    /// </summary>
    /// <param name="task"></param>
    /// <returns></returns>
    public async Task<bool> Remove(AppTask task)
    {
        var isJob = await IsQuartzJob(task.TaskName, task.GroupName);
        var taskModel = await _storage.GetTaskByName(task.TaskName, task.GroupName);
        if (isJob.success)
        {
            //开始移除触发器
            IScheduler scheduler = await _schedulerFactory.GetScheduler();

            ITrigger trigger = await JobExtension.FindTrigger(scheduler, task.GroupName, task.TaskName);

            await scheduler.PauseTrigger(trigger.Key);
            await scheduler.UnscheduleJob(trigger.Key); // 移除触发器
            await scheduler.DeleteJob(trigger.JobKey);
        }

        if (taskModel != null)
        {
            await _storage.Remove(task);
        }

        return true;
    }

    /// <summary>
    /// 暂停任务
    /// </summary>
    /// <param name="task"></param>
    /// <returns></returns>
    public async Task<bool> Pause(AppTask task)
    {
        var isJob = await IsQuartzJob(task.TaskName, task.GroupName);
        var taskModel = await _storage.GetTaskByName(task.TaskName, task.GroupName);
        string message = "";
        if (isJob.success)
        {
            //开始移除触发器
            IScheduler scheduler = await _schedulerFactory.GetScheduler();

            ITrigger trigger = await JobExtension.FindTrigger(scheduler, task.GroupName, task.TaskName);

            await scheduler.PauseTrigger(trigger.Key);
            message += "Quartz已暂停";
        }

        if (taskModel != null)
        {
            task.Status = (int)JobState.暂停;
            await _storage.Update(task);
        }

        return true;
    }

    /// <summary>
    /// 判断是否在quartz job 中
    /// </summary>
    /// <param name="taskName"></param>
    /// <param name="groupName"></param>
    /// <returns></returns>
    private async Task<(bool success, string message)> IsQuartzJob(string taskName, string groupName)
    {
        string errorMsg = "";
        IScheduler scheduler = await _schedulerFactory.GetScheduler();


        JobKey jobKey = await JobExtension.FindJobKey(scheduler, groupName, taskName);


        if (jobKey == null)
        {
            errorMsg = $"未找到任务{taskName}]";
            return (false, errorMsg);
        }

        ITrigger trigger = await JobExtension.FindTrigger(scheduler, jobKey);

        if (trigger == null)
        {
            errorMsg = $"未找到触发器[{taskName}]";
            return (false, errorMsg);
        }

        return (true, "");
    }

    /// <summary>
    /// 判断cron 的正确性
    /// </summary>
    /// <param name="cronExpression"></param>
    /// <exception cref="MyException"></exception>
    public void IsValidExpression(string cronExpression)
    {
        CronTriggerImpl trigger = new CronTriggerImpl();
        trigger.CronExpressionString = cronExpression;
        DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
        var isCron = date != null;
        if (!isCron)
        {
            throw new ArgumentInvalidException($"请确认表达式{cronExpression}是否正确!");
        }
    }
}